package com.zdft.promotion.common.distributelock;

import com.zdft.promotion.common.service.BusinessException;
import com.zdft.promotion.sso.util.AuthenticationUtil;
import com.zdft.promotion.sso.vo.UserBaseVO;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class DistributeLockAspect {

    private final static String LOCKER_PREFIX = "lock:";

    @Autowired
    private LockWorker lockWorker;

    @Autowired
    RedissonClient redissonClient;

    @Pointcut("@annotation(com.zdft.promotion.common.distributelock.DistributeLock)")
    public void DistributeLock() {

    }

    @Around("DistributeLock()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);

        if (distributeLock != null) {
            if (StringUtils.isBlank(distributeLock.value())) {
                throw new BusinessException("10");
            }
        }
        String lockName = distributeLock.value();
        long lockTime = distributeLock.lockTime();
        LockLevel level = distributeLock.level();

        UserBaseVO userBaseVO = AuthenticationUtil.get().getUserBase();
        if (level.equals(LockLevel.personal)) {
            lockName = lockName + userBaseVO.getEmpCode();//私人级锁，防止前端页面重复提交
        } else if (level.equals(LockLevel.dept)) {
            lockName = lockName + userBaseVO.getDeptName();//部门级锁，防止部门多人同时操作某功能
        }

        RLock lock = redissonClient.getLock(LOCKER_PREFIX + lockName);
        boolean success = lock.tryLock(lockTime, lockTime, TimeUnit.SECONDS);//等待时间和持锁时间保持相等
        if (success) {
            try {
                return joinPoint.proceed();
            } finally {
                lock.unlock();
            }
        }
        return null;
    }
}

